package fr.upmc.alasca.centreCalcul.externe.generateurRequetes;

import java.util.Random;

import fr.upmc.alasca.centreCalcul.externe.generateurDemandes.connectors.GenerateurDemandesConnector;
import fr.upmc.alasca.centreCalcul.externe.generateurDemandes.interfaces.GenerateurDemandesI;
import fr.upmc.alasca.centreCalcul.externe.generateurDemandes.ports.GenerateurDemandesOutboundPort;
import fr.upmc.alasca.centreCalcul.interne.repartiteurRequetes.connectors.RepartiteurRequetesClientConnector;
import fr.upmc.alasca.centreCalcul.interne.repartiteurRequetes.interfaces.RepartiteurRequetesClientI;
import fr.upmc.alasca.centreCalcul.interne.repartiteurRequetes.ports.RepartiteurRequetesClientOutboundPort;
import fr.upmc.alasca.centreCalcul.objets.Requete;
import fr.upmc.alasca.centreCalcul.outils.LogMessage;
import fr.upmc.alasca.centreCalcul.outils.LoiNormale;
import fr.upmc.components.AbstractComponent;
import fr.upmc.components.exceptions.ComponentShutdownException;
import fr.upmc.components.exceptions.ComponentStartException;


/**
 * Composant simulant l'envoi de requêtes.
 * @author Antoine Lavaire, Sabri Labassi, Thibaut Fleury, Ryme Fantazi
 */
public	class	GenerateurRequetes
extends			AbstractComponent
{
	/* ------------------------------------------------------------- ATTRIBUT */
	protected	boolean									estDistribue;

	protected	String									idApplication;
	protected	long									nbRequetes;
	protected	long									moyenneInstructions;
	protected	long									ecartTypeInstructions;
	protected	GenerateurDemandesOutboundPort			generateurDemandes;
	protected	RepartiteurRequetesClientOutboundPort	repartiteurRequetes;
	protected	boolean									arret;




	/* --------------------------------------------------------- CONSTRUCTEUR */
	public	GenerateurRequetes
	(
		String		p_idApplication,
		boolean		p_estDistribue,
		long		p_nbRequetes,
		long		p_moyenneInstructions,
		long		p_ecartTypeInstructions,
		String		p_generateurDemandesUri,
		String		p_repartiteurRequeteUri
	) throws	Exception
	{
		super(true);
		
		assert p_nbRequetes > 0;
		assert p_moyenneInstructions > 0;
		assert p_ecartTypeInstructions > 0.0;
		
		estDistribue = p_estDistribue;
		
		idApplication = p_idApplication;
		nbRequetes = p_nbRequetes;
		moyenneInstructions = p_moyenneInstructions;
		ecartTypeInstructions = p_ecartTypeInstructions;
		
		arret = false;
		
		// Ajout de l'interface requise pour l'utilisation du générateur de demandes
		this.addRequiredInterface(GenerateurDemandesI.class);
		generateurDemandes = new GenerateurDemandesOutboundPort(p_generateurDemandesUri + "_Outbound", this);
		this.addPort(generateurDemandes);
		generateurDemandes.localPublishPort();
		generateurDemandes.doConnection(p_generateurDemandesUri, GenerateurDemandesConnector.class.getName());
		
		// Ajout de l'interface requise pour l'utilisation du répartiteur de requêtes
		this.addRequiredInterface(RepartiteurRequetesClientI.class);
		repartiteurRequetes = new RepartiteurRequetesClientOutboundPort(p_repartiteurRequeteUri + "_Client_Outbound", this);
		this.addPort(repartiteurRequetes);
		repartiteurRequetes.localPublishPort();
		repartiteurRequetes.doConnection(p_repartiteurRequeteUri + "_Client", RepartiteurRequetesClientConnector.class.getName());
		
		assert repartiteurRequetes != null && repartiteurRequetes instanceof RepartiteurRequetesClientI;
	}




	/* -------------------------------------------------------------- METHODE */
	@Override
	public	void	start
	() throws	ComponentStartException
	{
		super.start();
		
		this.runTask(
			new ComponentTask() {
				@Override
				public void run() {
					try {
						genererRequete();
					}
					catch(Exception e) {
						//e.printStackTrace();
						System.err.println("Générateur de requêtes " + idApplication + " :\t\texception levée. Arrêt de l'application.");
						try {
							generateurDemandes.arreterApplication(idApplication);
						}
						catch(Exception e1) {
							//e1.printStackTrace();
						}
					}
				}
			}
		);
	}


	/**
	 * Génère <em>nbRequetes</em> requêtes d'en moyenne
	 * <em>moyenneInstructions</em> instructions à intervalle non régulier
	 * (entre 0 et 500 millisecondes).
	 * Pendant la durée de vie du <em>GenerateurRequetes</em>, le nombre
	 * d'instructions moyen va changer à plusieurs reprises.
	 * @throws Exception
	 */
	public	void	genererRequete
	() throws	Exception
	{
		int changementDeRythme_1 = (int) (nbRequetes / 5);
		int changementDeRythme_2 = changementDeRythme_1 * 2;
		int changementDeRythme_3 = changementDeRythme_1 * 3;
		int changementDeRythme_4 = changementDeRythme_1 * 4;
		
		for(int i = 0; i < nbRequetes; i++) {
			// Arret de l'application
			if(arret)
				break;
			
			if(i == changementDeRythme_1) {
				// On double le nombre d'instructions par requête
				moyenneInstructions *= 2;
				LogMessage.printMessage("Requêtes", idApplication,
						"20% des requêtes envoyées.");
			}
			else if(i == changementDeRythme_2 || i == changementDeRythme_3 || i == changementDeRythme_4) {
				// Variation aléatoire du nombre d'instructions par requête
				moyenneInstructions *= (new Random().nextFloat() + 0.5); // 0.5 * MI < MI < 1.5 * MI
				LogMessage.printMessage("Requêtes", idApplication,
						(i * 20 / changementDeRythme_1) + "% des requêtes envoyées.");
			}
			
			long nbInstructions = LoiNormale.getNext(moyenneInstructions, ecartTypeInstructions);
			Requete requete = new Requete(idApplication, i, nbInstructions);
			
			//System.out.println("Générateur de requêtes :\t\tapplication " + idApplication + ", " + nbInstructions + " instructions.");
			
			repartiteurRequetes.soumettreRequete(requete);
			
			Thread.sleep((int) (new Random().nextFloat() * 500));
		}
		
		LogMessage.printMessage("Requêtes", idApplication,
				"Demande d'arrêt de l'application.");
		generateurDemandes.arreterApplication(idApplication);
	}




	@Override
	public	void	shutdown() throws ComponentShutdownException {
		arret = true;
		
		super.shutdown();
	}
}
